package com.yeziji.job.business.jobLog.service.impl;

import cn.hutool.core.util.StrUtil;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryChain;
import com.yeziji.common.IServiceImpl;
import com.yeziji.job.business.jobInfo.base.JobInfo;
import com.yeziji.job.business.jobInfo.entity.JobInfoEntity;
import com.yeziji.job.business.jobInfo.entity.table.JobInfoTableDef;
import com.yeziji.job.business.jobLog.dto.JobLogDTO;
import com.yeziji.job.business.jobLog.dto.params.JobLogQueryDTO;
import com.yeziji.job.business.jobLog.entity.JobLogEntity;
import com.yeziji.job.business.jobLog.entity.table.JobLogTableDef;
import com.yeziji.job.business.jobLog.mapper.JobLogMapper;
import com.yeziji.job.business.jobLog.service.JobLogService;
import com.yeziji.job.common.base.JobContextInfo;
import com.yeziji.job.constant.JobExecuteResultCodeEnum;
import com.yeziji.job.constant.JobNoticeStatusEnum;
import com.yeziji.utils.GzipUtils;
import com.yeziji.utils.expansion.Lists2;
import com.yeziji.utils.expansion.Opt2;
import com.yeziji.utils.expansion.Str2;
import org.quartz.Trigger;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * 任务日志 服务层实现。
 *
 * @author system
 * @since 2024-06-27
 */
@Service
public class JobLogServiceImpl extends IServiceImpl<JobLogMapper, JobLogEntity> implements JobLogService {
    private static final JobLogTableDef JOB_LOG_TABLE_DEF = JobLogTableDef.JOB_LOG;
    private static final JobInfoTableDef JOB_INFO_TABLE_DEF = JobInfoTableDef.JOB_INFO;

    private static QueryChain<JobLogEntity> getJobLogDtoMapperQueryChain(JobLogQueryDTO queryDTO) {
        final Long jobId = queryDTO.getJobId();
        final Collection<Long> jobIds = queryDTO.getJobIds();
        final Integer executeResultCode = queryDTO.getExecuteResultCode();
        final String executeFlag = Str2.nullEmpty(queryDTO.getExecuteFlag());

        return QueryChain.of(JobLogEntity.class)
                .select(JOB_LOG_TABLE_DEF.DEFAULT_COLUMNS)
                .select(JOB_INFO_TABLE_DEF.NAME.as("jobName"), JOB_INFO_TABLE_DEF.GROUP_NAME.as("jobGroupName"))
                .join(JobInfoEntity.class).on((queryWrapper) -> queryWrapper.where(JobInfoEntity::getId).eq(JobLogEntity::getJobId))
                .where(JobLogEntity::getExecuteFlag).likeLeft(executeFlag, StrUtil.isNotBlank(executeFlag))
                .and(JobLogEntity::getExecuteResultCode).eq(executeResultCode, executeResultCode != null)
                .and(JobLogEntity::getJobId).eq(jobId, jobId != null)
                .and(JobLogEntity::getJobId).in(jobIds, Lists2.isNotEmpty(jobIds));
    }

    @Override
    public void saveByJobContextInfo(JobContextInfo jobContextInfo) {
        this.save(this.jobContextInfoConvertToEntity(jobContextInfo));
    }

    @Override
    public Page<JobLogDTO> pageAsDto(JobLogQueryDTO queryDTO) {
        return getJobLogDtoMapperQueryChain(queryDTO).pageAs(queryDTO.getOrDefaultPage(), queryDTO.getQueryClass());
    }

    private JobLogEntity jobContextInfoConvertToEntity(JobContextInfo jobContextInfo) {
        JobInfo jobInfo = jobContextInfo.getJobInfo();
        if (jobInfo == null) {
            return null;
        }

        String flag = jobContextInfo.getFlag();
        String logMsg = jobContextInfo.getLogMsg();
        String triggerIp = jobContextInfo.getTriggerIp();
        Integer retryCount = jobContextInfo.getRetryCount();
        String exceptionMsg = jobContextInfo.getExceptionMsg();
        Trigger.TriggerState status = jobContextInfo.getStatus();
        Date executeTime = Opt2.nullElse(jobContextInfo.getExecuteTime(), new Date());

        return JobLogEntity.builder()
                .jobId(jobInfo.getId())
                .msg(this.getLogMsg(logMsg, exceptionMsg))
                .executeIp(triggerIp)
                .executeFlag(flag)
                .executeParams(jobInfo.getInvokeParams())
                .executeResultCode(JobExecuteResultCodeEnum.getByTriggerState(status).getCode())
                .retryCount(retryCount)
                .executeTime(executeTime)
                // TODO: 任务通知
                .noticeStatus(JobNoticeStatusEnum.NONE.getCode())
                .build();
    }

    /**
     * 获取日志信息
     *
     * @param msg      日志信息
     * @param messages 日志信息
     * @return {@link Byte[]} gzip 压缩字節
     */
    private byte[] getLogMsg(String msg, String... messages) {
        StringJoiner sj = new StringJoiner("\n");
        sj.add(Str2.nullEmpty(msg));

        // messages add
        Arrays.stream(messages).filter(Objects::nonNull).forEach(sj::add);
        if (sj.toString().isBlank()) {
            return null;
        }
        return GzipUtils.compress(sj.toString());
    }
}